// 自旋锁实现
#include "spinlock.h"
#include "riscv.h"
#include "common.h"
#include "proc.h"

// 初始化自旋锁
void initlock(struct spinlock *lk, char *name) {
    lk->locked = 0;
    lk->name = name;
    lk->cpu = 0;
}
// 获取中断状态: 0: 关闭 1: 开启
int sie_get(void) {
    uint64 sstatus = r_csr(SSTATUS);
    return (sstatus & SSTATUS_SIE) != 0;
}

// 嵌套关闭中断，记录调用次数，确保调用者全部完成再恢复中断
// 与pop_off()对应
void intr_push(void) {
    int sie = sie_get();   // 获取当前中断状态
    intr_off();            // 关闭中断，保证接下来操作的原子性
    // 如果中断关闭，记录中断嵌套次数
    struct cpu *c = getcpu();
    if(c->intrdep == 0) {  // 记录第一次嵌套中断时的中断状态
        c->intrprev = sie;
    }
    c->intrdep++;
}
// 嵌套开启中断(此时处于中断关闭状态，因此一定是原子操作)
void intr_pop(void) {
    if(sie_get() == 1) {
        panic("intr_pop：中断状态错误！");
    }
    struct cpu *c = getcpu();
    if(c->intrdep < 1) {
        panic("intr_pop：中断嵌套深度错误！");
    }
    c->intrdep--;
    // 中断嵌套深度为0，且嵌套之前中断就是开启的，则开启中断
    if(c->intrdep == 0 && c->intrprev == 1) {
        intr_on();
    }
}

// 尝试获取自旋锁
void acquire(struct spinlock *lk) {
    // 关闭中断，获取锁的过程中被中断可能导致死锁
    intr_push();
    if(lk->locked == 1 && lk->cpu == getcpu()) {
        panic("acquired：已经持有锁！");
    }
    // 不断尝试获取锁，直到成功
    // 原子性指令，防止被其他CPU打断
    // 原来是1，交换后还是1，说明获取锁失败；原来是0，交换后是0，说明获取锁成功
    while(__sync_lock_test_and_set(&lk->locked, 1) != 0)
        ;
    __sync_synchronize();
    lk->cpu = getcpu();
}

// 释放自旋锁
void release(struct spinlock *lk) {
    if(lk->cpu != getcpu()) {
        printf("lk->cpu: %d, getcpu(): %d\n", lk->cpu, getcpu());
        panic("release：释放锁的CPU与持有锁的CPU不一致！");
    }
    lk->cpu = 0;
    __sync_synchronize();
    __sync_lock_release(&lk->locked);
    // 恢复中断
    intr_pop();
}